import socket
import json


class TCP_Send:
    def __init__(self, ip, port, timeout=0.0001) -> None:
        """
        The constructor of TCP_Send class \n
        :param ip: IP address of the target computer \n
        :param port: Port of the target computer \n
        :param timeout: Set connect timeout \n
        :return: None
        """
        self.ip = ip
        self.port = port
        self.timeout = timeout
        self.connect()
        # self.tcp_socket.recv()

    def connect(self):
        """
        尝试连接
        :return: 无
        """
        self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        print("Connecting......")
        try:
            # self.tcp_socket.connect((self.ip, self.port))
            self.tcp_socket.bind((self.ip, self.port))
            self.tcp_socket.listen(10)
            self.conn, addr = self.tcp_socket.accept()
            print(addr)
            self.conn.settimeout(self.timeout)
        except:
            self.tcp_socket.close()
            raise TimeoutError("Can not connect target!!!")
        print("Connecting successful!")

    def __convert_to_bytes__(self, tuple):
        """
        Convert a tuple to bytes \n
        :param tuple: Your tuple \n
        :return: A converted list
        """
        ret = []
        for i in tuple:
            for j in i:
                ret.append(j.to_bytes(2, byteorder="little", signed=False))  # Converting members of tuple to bytes
        return ret

    def __send(self, msg):
        """
        发送函数
        :param msg: 发送的消息
        :return: 无
        """
        try:
            self.conn.send(msg)
        except:
            try:
                self.connect()
                print("Reconnect......")
                print("Reconnect Seccessful")
                self.conn.send(msg)
            except:
                raise ConnectionError("Can not connect target!!!")

    def send_msg(self, msg, mod):
        """
        Send a message to the target computer \n
        :param msg: Your message tuple \n
        :param mod: Your message type \n
        :return: None
        """
        if mod == 0:
            msg_ = self.__encode(msg, "connection_bone")
        elif mod == 1:
            msg_ = self.__encode(msg, "angle")
        elif mod == 2:
            msg_ = self.__encode(msg, "landmark")
        elif mod == 3:
            msg_ = self.__encode(msg, "times")
        else:
            raise ValueError("Please enter a right parameter")

        print("Start sending!")
        self.__send(0x4c.to_bytes(1, byteorder="little", signed=False))
        self.__send(0x45.to_bytes(1, byteorder="little", signed=False))
        if mod == 0:
            self.__send(0x01.to_bytes(1, byteorder="little", signed=False))
        elif mod == 1:
            self.__send(0x03.to_bytes(1, byteorder="little", signed=False))
        elif mod == 2:
            self.__send(0x02.to_bytes(1, byteorder="little", signed=False))
        elif mod == 3:
            self.__send(0x05.to_bytes(1, byteorder="little", signed=False))
        else:
            print("mod error, please choose a right mod.")
            raise KeyError("mod error, please choose a right mod.")

        # if mod != 3:
        #     len_of_msg = len(msg_) * 2
        # else:
        len_of_msg = len(msg_)

        self.__send(len_of_msg.to_bytes(4, byteorder="little", signed=False))

        self.__send(0x4c.to_bytes(1, byteorder="little", signed=False))
        self.__send(0x45.to_bytes(1, byteorder="little", signed=False))

        # print(msg_)
        self.__send(msg_)
        print("Send finish")


    def __recv(self, bufsize):
        try:
            rec = self.conn.recv(bufsize)
            print(rec)
            rec = str(rec, encoding='utf8')
        except ConnectionResetError:
            try:
                self.connect()
                print("Reconnect......")
                print("Reconnect Seccessful")
                rec = self.tcp_socket.recv(bufsize)
            except:
                raise ConnectionError("Can not connect target!!!")
        except:
            print("timeout")
            return -1
        return rec

    def recv(self):
        header = self.__recv(1)
        if header != "L":
            return -1

        header = self.__recv(1)

        if header != "E":
            return -1

        def bit_to_int(bits):
            ret = ''
            for bit in bits:
                tp = '{:08b}'.format(ord(bit))
                ret += tp
            return int(ret, 2)

        cls = self.__recv(1)
        # print(bit_to_int(cls))
        if bit_to_int(cls) != 4:
            return -1

        length = self.__recv(4)
        if length == -1:
            return -1

        header = self.__recv(1)
        if header != "L":
            return -1
        header = self.__recv(1)
        if header != "E":
            return -1

        recv = self.__recv(bit_to_int(length))
        try:
            recv = self.__decode(recv)
        except:
            return -1
        return recv

    def __decode(self, msg):
        msg = json.loads(msg)
        return msg

    def __encode(self, msg, key):
        send_dict = {key: msg}
        send_dict = json.dumps(send_dict)
        send_dict = str(send_dict).encode("utf8")
        # print(send_dict)
        return send_dict

    def close_socket(self):
        """
        Close socket \n
        :return: None
        """
        self.tcp_socket.close()
